{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-04-30T07:08:26.633661400Z",
     "start_time": "2024-04-30T07:08:22.057842200Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "sys.version_info(major=3, minor=9, micro=7, releaselevel='final', serial=0)\n",
      "matplotlib 3.8.4\n",
      "numpy 1.26.4\n",
      "pandas 2.2.2\n",
      "sklearn 1.4.2\n",
      "torch 2.2.2+cpu\n",
      "cpu\n"
     ]
    }
   ],
   "source": [
    "import matplotlib as mpl\n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline\n",
    "import numpy as np\n",
    "import sklearn\n",
    "import pandas as pd\n",
    "import os\n",
    "import sys\n",
    "import time\n",
    "from tqdm.auto import tqdm\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "\n",
    "print(sys.version_info)\n",
    "for module in mpl, np, pd, sklearn, torch:\n",
    "    print(module.__name__, module.__version__)\n",
    "    \n",
    "device = torch.device(\"cuda:0\") if torch.cuda.is_available() else torch.device(\"cpu\")\n",
    "print(device)\n",
    "\n",
    "seed = 42\n"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 准备数据"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-04-30T07:08:38.695073400Z",
     "start_time": "2024-04-30T07:08:29.279970900Z"
    }
   },
   "outputs": [],
   "source": [
    "from tensorflow import keras #这里的波浪线不用管\n",
    "#用karas有的数据集imdb，电影分类,分电影是积极的，还是消极的\n",
    "imdb = keras.datasets.imdb\n",
    "#载入数据使用下面两个参数\n",
    "vocab_size = 10000  #词典大小，仅保留训练数据中前10000个最经常出现的单词，低频单词被舍弃\n",
    "index_from = 3  #0,1,2,3空出来做别的事\n",
    "#前一万个词出现词频最高的会保留下来进行处理，后面的作为特殊字符处理，\n",
    "# 小于3的id都是特殊字符，下面代码有写\n",
    "# 需要注意的一点是取出来的词表还是从1开始的，需要做处理\n",
    "(train_data, train_labels), (test_data, test_labels) = imdb.load_data(\n",
    "    num_words = vocab_size, index_from = index_from)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "outputs": [
    {
     "data": {
      "text/plain": "numpy.ndarray"
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "type(train_labels)"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-04-30T07:10:30.260237600Z",
     "start_time": "2024-04-30T07:10:30.219261100Z"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class 'list'>\n"
     ]
    }
   ],
   "source": [
    "print(type(train_data))"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-04-30T07:11:03.361414700Z",
     "start_time": "2024-04-30T07:11:03.344409600Z"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "outputs": [
    {
     "data": {
      "text/plain": "[1,\n 14,\n 47,\n 8,\n 30,\n 31,\n 7,\n 4,\n 249,\n 108,\n 7,\n 4,\n 5974,\n 54,\n 61,\n 369,\n 13,\n 71,\n 149,\n 14,\n 22,\n 112,\n 4,\n 2401,\n 311,\n 12,\n 16,\n 3711,\n 33,\n 75,\n 43,\n 1829,\n 296,\n 4,\n 86,\n 320,\n 35,\n 534,\n 19,\n 263,\n 4821,\n 1301,\n 4,\n 1873,\n 33,\n 89,\n 78,\n 12,\n 66,\n 16,\n 4,\n 360,\n 7,\n 4,\n 58,\n 316,\n 334,\n 11,\n 4,\n 1716,\n 43,\n 645,\n 662,\n 8,\n 257,\n 85,\n 1200,\n 42,\n 1228,\n 2578,\n 83,\n 68,\n 3912,\n 15,\n 36,\n 165,\n 1539,\n 278,\n 36,\n 69,\n 2,\n 780,\n 8,\n 106,\n 14,\n 6905,\n 1338,\n 18,\n 6,\n 22,\n 12,\n 215,\n 28,\n 610,\n 40,\n 6,\n 87,\n 326,\n 23,\n 2300,\n 21,\n 23,\n 22,\n 12,\n 272,\n 40,\n 57,\n 31,\n 11,\n 4,\n 22,\n 47,\n 6,\n 2307,\n 51,\n 9,\n 170,\n 23,\n 595,\n 116,\n 595,\n 1352,\n 13,\n 191,\n 79,\n 638,\n 89,\n 2,\n 14,\n 9,\n 8,\n 106,\n 607,\n 624,\n 35,\n 534,\n 6,\n 227,\n 7,\n 129,\n 113]"
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "train_data[2]"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-04-30T08:10:59.228614700Z",
     "start_time": "2024-04-30T08:10:59.204627200Z"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "outputs": [
    {
     "data": {
      "text/plain": "array([1, 0, 0, 1, 0, 0, 1, 0, 1, 0], dtype=int64)"
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "train_labels[0:10]"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-04-30T07:11:32.047100700Z",
     "start_time": "2024-04-30T07:11:32.020114800Z"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "train 25000 (25000,)\n",
      "test 25000 (25000,)\n"
     ]
    }
   ],
   "source": [
    "print(\"train\", len(train_data), train_labels.shape) #25000个样本，每个样本是一段话，每个单词用一个数字表示\n",
    "print(\"test\", len(test_data), test_labels.shape)"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-04-30T07:10:50.518351600Z",
     "start_time": "2024-04-30T07:10:50.480372700Z"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-04-30T08:08:10.708061700Z",
     "start_time": "2024-04-30T08:08:10.574140400Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "88584\n",
      "<class 'dict'>\n"
     ]
    }
   ],
   "source": [
    "#载入词表，看下词表长度，词表就像英语字典\n",
    "word_index = imdb.get_word_index()\n",
    "print(len(word_index))\n",
    "print(type(word_index))\n",
    "#词表虽然有8万多，但是我们只载入了最高频的1万词！！！！"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 构造 word2idx 和 idx2word"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-04-30T08:08:13.542197800Z",
     "start_time": "2024-04-30T08:08:13.442229600Z"
    }
   },
   "outputs": [],
   "source": [
    "word2idx = {word: idx + 3 for word, idx in word_index.items()} # 0,1,2,3空出来做别的事,这里的idx是从1开始的,所以加3\n",
    "word2idx.update({\n",
    "    \"[PAD]\": 0,     # 填充 token\n",
    "    \"[BOS]\": 1,     # begin of sentence\n",
    "    \"[UNK]\": 2,     # 未知 token\n",
    "    \"[EOS]\": 3,     # end of sentence\n",
    "})\n",
    "\n",
    "idx2word = {idx: word for word, idx in word2idx.items()} # 反向词典,id变为单词"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-12-07T08:05:05.850024400Z",
     "start_time": "2023-12-07T08:05:04.110751600Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": "<Figure size 432x288 with 1 Axes>",
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXcAAAD4CAYAAAAXUaZHAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/YYfK9AAAACXBIWXMAAAsTAAALEwEAmpwYAAAdQUlEQVR4nO3de5BV5bnn8e9DH46EoAYFheKSpiMQLjJcWuIFAQ9REc0hUITBshQ0E0wNVq4TDyblxJqpU5U5uViVzIxTpBTU8uBBLoLCITQUl1CK2ihBkHBvpTvcJSgiocFn/ujVnE3bDbt77+Zd796/T1VXr/3utff6vXs3D6ufvXotc3dERKSwtAkdQERE8k/FXUSkAKm4i4gUIBV3EZECpOIuIlKA/i50AIBOnTp5aWlp6BgiIlHZuHHjEXfv3Nh9qSjupaWlVFZWho4RhePHjwNw5ZVXBk4iIqGZ2ftN3ae2TGQWLVrEokWLQscQkZS7aHE3sx5mttrM3jOzrWb2/WT8KjOrMLOdyfeOybiZ2W/NbJeZbTazoa09iWIycuRIRo4cGTqGiKRcNnvuZ4Afu3t/4EZghpn1B2YCq9y9N7AquQ1wF9A7+ZoOPJX31EWsrKyMsrKy0DFEJOUu2nN39/3A/mT5YzPbBnQDxgOjk9WeBdYA/5SMP+d15zXYYGZfMrOuyfNIjo4dOwZAx44dAyeRYlZbW0t1dTWnTp0KHaUotGvXju7du9O2bdusH9OsD1TNrBQYArwBXJtRsA8A1ybL3YB9GQ+rTsbOK+5mNp26PXt69uzZnBhFbfHixQBMmzYtbBApatXV1Vx++eWUlpZiZqHjFDR35+jRo1RXV9OrV6+sH5d1cTezDsAC4Afu/lHmG+rubmbNOgOZu88CZgGUl5fr7GVZGj16dOgIIpw6dUqF/RIxM66++moOHz7crMdlVdzNrC11hf0Fd1+YDB+sb7eYWVfgUDJeA/TIeHj3ZEzyQH8PIGmhwn7ptOS1zuZoGQOeBra5+28y7loCTE2WpwKLM8YfSI6auRE4rn57/hw5coQjR46EjiEiKZfN0TK3APcD/2Bmm5KvccAvgNvNbCfw9eQ2wDJgD7AL+D3wX/Mfu3i9+uqrvPrqq6FjiBS8NWvWcM899+T9eV9++WXee++9c7dHjx7dKn/Emc3RMuuBpn4nGNPI+g7MyDGXNGHMmM+95CISkZdffpl77rmH/v37t+p29BeqkenRowc9evS4+IoiBayqqoqvfvWrTJs2jT59+nDfffexcuVKbrnlFnr37s2bb74JwJtvvslNN93EkCFDuPnmm9m+fTsATz75JA899BAA7777LgMHDuTkyZNNbu+TTz7hoYceYvjw4QwZMuTcUWtz5sxh4sSJjB07lt69e/Poo4+ee8zTTz9Nnz59GD58ON/5znd45JFHeO2111iyZAk/+clPGDx4MLt37wbgpZdeYvjw4fTp04c//vGP+XmR3D3417Bhw1yyc/DgQT948GDoGFLk3nvvvfNuz54929955x13dz9z5ozPnj3b//SnP7m7++nTp3327Nn+7rvvurv7p59+6rNnzz73HJ988onPnj3b//znP7u7+8cff3zR7e/du9dLSkp88+bNfvbsWR86dKg/+OCD/tlnn/nLL7/s48ePd3f348ePe21trbu7V1RU+MSJE93d/ezZs37rrbf6woULfdiwYb5+/frPbWP16tV+9913u7v7Y4895s8//7y7ux87dsx79+7tJ06c8NmzZ3uvXr38r3/9q3/66afes2dP/+CDD7ympsa//OUv+9GjR/306dM+YsQInzFjhru7T5061V966aVz2xk1apT/6Ec/cnf3pUuX+pgxY7J6zd3dgUpvoq6m4sRhkr1ly5YBOs5dpFevXlx//fUADBgwgDFjxmBmXH/99VRVVQF1J9qbOnUqO3fuxMyora0FoE2bNsyZM4dBgwbx8MMPc8stt1xwWytWrGDJkiX86le/AuoOBf3ggw+AulZp/Yn8+vfvz/vvv8+RI0cYNWoUV111FQDf+ta32LFjR5PPP3HiRACGDRt2LnuuVNwjc/vtt4eOIPI5mTsbJSUl591u27btebfbtWt33u327dufd7tDhw5ZbfOyyy47t9ymTZtzt9u0acOZM2cAePzxx7nttttYtGgRVVVV5/2dyM6dO+nQoQN/+ctfLrotd2fBggX07dv3vPE33njjvBwlJSXntt0c9c/R0sc3Rj33yHTr1o1u3bqFjiEShePHj5/79zJnzpzzxr/3ve+xbt06jh49yvz58y/4PHfeeSe/+93vqOuEwDvvvHPB9W+44QbWrl3LsWPHOHPmDAsWLDh33+WXX87HH3/cwhllT8U9MgcOHODAgQOhY4hE4dFHH+Wxxx5j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\n"
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 选择 max_length\n",
    "length_collect = {}\n",
    "for text in train_data:\n",
    "    length = len(text) #句子长度\n",
    "    length_collect[length] = length_collect.get(length, 0) + 1 #统计长度的频率\n",
    "    \n",
    "MAX_LENGTH = 500\n",
    "plt.bar(length_collect.keys(), length_collect.values()) #长度分布图\n",
    "plt.axvline(MAX_LENGTH, label=\"max length\", c=\"gray\", ls=\":\") #画一条线，可以看到大部分都在500以内\n",
    "plt.legend()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "outputs": [
    {
     "data": {
      "text/plain": "<Figure size 640x480 with 1 Axes>",
      "image/png": "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V6lU6NChA6xWa6XiaNOmjc2xAaCwsBBBQUFVPykikgQTIiKqtW7cuAGVSoXMzEyoVCqbsnr16onbGo3GpkyhUEAQBIfFcffxFQoFAFQ6mSIi58AxRERUa7Vv3x4WiwWFhYUICQmxednr2rLHy8sLvr6+OHjwoLjPYrHg119/tamn1WphsVgcGj8ROQ+2EBFRrfXII48gLi4OQ4YMwZw5c9C+fXtcvnwZ27dvR5s2bRAbG1up44wfPx4pKSkICQlBq1at8Mknn+DatWtiaw8ANGvWDPv378e5c+dQr149NGjQoLpOi4gkwBYiIqrVli5diiFDhmDSpElo2bIl+vTpg4MHD1Zp/M60adPw4osvYsiQIdDr9ahXrx5iYmJsBkVPnjwZKpUKYWFhaNy4MXJzc6vjdIhIIgrBkR3pREQyYLVaERoaihdeeAHvvPOO1OEQUQ1glxkRubzz589jy5YtePLJJ1FaWor58+cjJycHgwYNkjo0Iqoh7DIjIpenVCqRlpaGxx57DJ06dcLRo0exbds2hIaGSh0aEdUQdpkRERGRy2MLEREREbk8JkRERETk8pgQERERkctjQkREREQujwkRERERuTwmREREROTymBARERGRy2NCRERERC7v/wFadT0uwXSFWgAAAABJRU5ErkJggg=="
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "#想对句子长度画个直方图，看看长度分布\n",
    "length_list = [len(text) for text in train_data]\n",
    "plt.hist(length_list, bins=50)\n",
    "plt.xlabel(\"length\")\n",
    "plt.ylabel(\"frequency\")\n",
    "plt.axvline(500, label=\"max length\", c=\"gray\", ls=\":\")\n",
    "plt.legend()\n",
    "plt.show()"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-04-30T07:22:06.029392800Z",
     "start_time": "2024-04-30T07:22:04.861183900Z"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "outputs": [
    {
     "data": {
      "text/plain": "[218, 189, 141, 550, 147, 43, 123, 562, 233, 130]"
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "length_list[0:10]"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-04-30T07:23:13.208447100Z",
     "start_time": "2024-04-30T07:23:13.168475700Z"
    }
   }
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Tokenizer"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-04-30T08:08:21.518577400Z",
     "start_time": "2024-04-30T08:08:21.491586500Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "raw text\n",
      "['hello', 'world']\n",
      "['tokenize', 'text', 'datas', 'with', 'batch']\n",
      "['this', 'is', 'a', 'test']\n",
      "indices\n",
      "tensor([   1, 4825,  182,    3,    0,    0,    0])\n",
      "tensor([    1,     2,  3004,     2,    19, 19233,     3])\n",
      "tensor([   1,   14,    9,    6, 2181,    3,    0])\n",
      "decode text\n",
      "[BOS] hello world [EOS] [PAD] [PAD] [PAD]\n",
      "[BOS] [UNK] text [UNK] with batch [EOS]\n",
      "[BOS] this is a test [EOS] [PAD]\n"
     ]
    }
   ],
   "source": [
    "class Tokenizer:\n",
    "    def __init__(self, word2idx, idx2word, max_length=500, pad_idx=0, bos_idx=1, eos_idx=3, unk_idx=2):\n",
    "        self.word2idx = word2idx #词表,单词到id\n",
    "        self.idx2word = idx2word #词表，id到单词\n",
    "        self.max_length = max_length\n",
    "        self.pad_idx = pad_idx #填充\n",
    "        self.bos_idx = bos_idx #开始\n",
    "        self.eos_idx = eos_idx #结束\n",
    "        self.unk_idx = unk_idx #未知，未出现在最高频词表中的词\n",
    "    \n",
    "    def encode(self, text_list):\n",
    "        \"\"\"\n",
    "        将文本列表转化为索引列表\n",
    "        :param text_list:当前批次的文本列表\n",
    "        :return:\n",
    "        \"\"\"\n",
    "        max_length = min(self.max_length, 2 + max([len(text) for text in text_list])) #最大长度，最大长度是500，但是如果句子长度小于500，就取句子长度（句子长度是本组句子中最长的），2是为了留出开始和结束的位置\n",
    "        indices = []\n",
    "        for text in text_list:\n",
    "            index = [self.word2idx.get(word, self.unk_idx) for word in text] #单词转化为id，未知的词用unk_idx代替\n",
    "            index = [self.bos_idx] + index + [self.eos_idx] #添加开始和结束\n",
    "            if len(index) < max_length:\n",
    "                index = index + [self.pad_idx] * (max_length - len(index)) #填充0\n",
    "            else:\n",
    "                index = index[:max_length] #如果句子长度大于500，就截断\n",
    "            indices.append(index)\n",
    "        return torch.tensor(indices) #二维列表转化为tensor\n",
    "    \n",
    "    def decode(self, indices_list, remove_bos=True, remove_eos=True, remove_pad=True, split=False):\n",
    "        \"\"\"\n",
    "        将索引列表转化为文本列表\n",
    "        :param indices_list:某批次的索引列表\n",
    "        :param remove_bos:\n",
    "        :param remove_eos:\n",
    "        :param remove_pad:\n",
    "        :param split:\n",
    "        :return:\n",
    "        \"\"\"\n",
    "        text_list = []\n",
    "        for indices in indices_list:\n",
    "            text = []\n",
    "            for index in indices:\n",
    "                word = self.idx2word.get(index, \"[UNK]\")\n",
    "                if remove_bos and word == \"[BOS]\":\n",
    "                    continue\n",
    "                if remove_eos and word == \"[EOS]\":\n",
    "                    break\n",
    "                if remove_pad and word == \"[PAD]\":\n",
    "                    break\n",
    "                text.append(word)\n",
    "            text_list.append(\" \".join(text) if not split else text)\n",
    "        return text_list\n",
    "    \n",
    "\n",
    "tokenizer = Tokenizer(word2idx=word2idx, idx2word=idx2word)\n",
    "raw_text = [\"hello world\".split(), \"tokenize text datas with batch\".split(), \"this is a test\".split()]\n",
    "indices = tokenizer.encode(raw_text) #encode支持批量处理\n",
    "decode_text = tokenizer.decode(indices.tolist(), remove_bos=False, remove_eos=False, remove_pad=False)\n",
    "print(\"raw text\")\n",
    "for raw in raw_text:\n",
    "    print(raw)\n",
    "print(\"indices\")\n",
    "for index in indices:\n",
    "    print(index)\n",
    "print(\"decode text\")\n",
    "for decode in decode_text:\n",
    "    print(decode)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-04-30T08:13:23.712045900Z",
     "start_time": "2024-04-30T08:13:23.682059800Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": "[\"[BOS] this film was just brilliant casting location scenery story direction everyone's really suited the part they played and you could just imagine being there robert [UNK] is an amazing actor and now the same being director [UNK] father came from the same scottish island as myself so i loved the fact there was a real connection with this film the witty remarks throughout the film were great it was just brilliant so much that i bought the film as soon as it was released for [UNK] and would recommend it to everyone to watch and the fly fishing was amazing really cried at the end it was so sad and you know what they say if you cry at a film it must have been good and this definitely was also [UNK] to the two little boy's that played the [UNK] of norman and paul they were just brilliant children are often left out of the [UNK] list i think because the stars that play them all grown up are such a big profile for the whole film but these children are amazing and should be praised for what they have done don't you think the whole story was so lovely because it was true and was someone's life after all that was shared with us all\",\n \"[BOS] big hair big boobs bad music and a giant safety pin these are the words to best describe this terrible movie i love cheesy horror movies and i've seen hundreds but this had got to be on of the worst ever made the plot is paper thin and ridiculous the acting is an abomination the script is completely laughable the best is the end showdown with the cop and how he worked out who the killer is it's just so damn terribly written the clothes are sickening and funny in equal [UNK] the hair is big lots of boobs [UNK] men wear those cut [UNK] shirts that show off their [UNK] sickening that men actually wore them and the music is just [UNK] trash that plays over and over again in almost every scene there is trashy music boobs and [UNK] taking away bodies and the gym still doesn't close for [UNK] all joking aside this is a truly bad film whose only charm is to look back on the disaster that was the 80's and have a good old laugh at how bad everything was back then\"]"
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 看看训练集的数据\n",
    "\n",
    "tokenizer.decode(train_data[:2], remove_bos=False, remove_eos=False, remove_pad=False)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 数据集与 DataLoader"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-04-30T08:21:19.059617900Z",
     "start_time": "2024-04-30T08:21:15.057333800Z"
    }
   },
   "outputs": [],
   "source": [
    "from torch.utils.data import Dataset, DataLoader\n",
    "\n",
    "class IMDBDataset(Dataset):\n",
    "    def __init__(self, data, labels, remain_length=True):\n",
    "        if remain_length: #字符串输出样本中，是否含有【BOS】和【EOS】，【PAD】\n",
    "            self.data = tokenizer.decode(data, remove_bos=False, remove_eos=False, remove_pad=False)\n",
    "        else:\n",
    "            # 缩减一下数据\n",
    "            self.data = tokenizer.decode(data)\n",
    "        self.labels = labels\n",
    "    \n",
    "    def __getitem__(self, index):\n",
    "        text = self.data[index]\n",
    "        label = self.labels[index]\n",
    "        return text, label\n",
    "    \n",
    "    def __len__(self):\n",
    "        return len(self.data)\n",
    "    \n",
    "    \n",
    "def collate_fct(batch):\n",
    "    \"\"\"\n",
    "    将batch数据处理成tensor形式\n",
    "    :param batch:\n",
    "    :return:\n",
    "    \"\"\"\n",
    "    text_list = [item[0].split() for item in batch]\n",
    "    label_list = [item[1] for item in batch]\n",
    "    text_list = tokenizer.encode(text_list).to(dtype=torch.int) # 文本转化为索引\n",
    "    return text_list, torch.tensor(label_list).reshape(-1, 1).to(dtype=torch.float)\n",
    "\n",
    "\n",
    "train_ds = IMDBDataset(train_data, train_labels)\n",
    "test_ds = IMDBDataset(test_data, test_labels)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-04-30T08:21:22.840483300Z",
     "start_time": "2024-04-30T08:21:22.833492200Z"
    }
   },
   "outputs": [],
   "source": [
    "batch_size = 128\n",
    "train_dl = DataLoader(train_ds, batch_size=batch_size, shuffle=True, collate_fn=collate_fct) #collate_fn是处理batch的函数\n",
    "test_dl = DataLoader(test_ds, batch_size=batch_size, shuffle=False, collate_fn=collate_fct)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 定义模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "outputs": [
    {
     "data": {
      "text/plain": "torch.Size([1, 2, 1])"
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# target output size of 5\n",
    "m = nn.AdaptiveAvgPool1d(1) # 自适应平均池化\n",
    "input = torch.randn(1, 2, 4)\n",
    "output = m(input)\n",
    "output.size() #可以看到最后一维变成了1"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-04-30T08:22:16.608867400Z",
     "start_time": "2024-04-30T08:22:16.586879400Z"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-04-30T08:35:20.187378600Z",
     "start_time": "2024-04-30T08:35:20.163393400Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "            embeding.weight             paramerters num: 160000\n",
      "              layer.weight              paramerters num: 1024\n",
      "               layer.bias               paramerters num: 64\n",
      "               fc.weight                paramerters num: 64\n",
      "                fc.bias                 paramerters num: 1\n"
     ]
    }
   ],
   "source": [
    "class AddingModel(nn.Module):\n",
    "    def __init__(self, embedding_dim=16, hidden_dim=64, vocab_size=vocab_size):\n",
    "        super(AddingModel, self).__init__()\n",
    "        self.embeding = nn.Embedding(vocab_size, embedding_dim) # 词嵌入\n",
    "        self.pool = nn.AdaptiveAvgPool1d(1) # 自适应平均池化,对应tf是全局平均值池化\n",
    "        self.layer = nn.Linear(embedding_dim, hidden_dim) # 全连接层\n",
    "        self.fc = nn.Linear(hidden_dim, 1) # 全连接层\n",
    "        \n",
    "    def forward(self, x):\n",
    "        # [bs, seq length] [128, 500]  [128,500,10000]--->[128,500,16]\n",
    "        x = self.embeding(x).transpose(1, 2)        # transpose [bs, seq length, embedding_dim] -> shape [bs, embedding_dim, seq length]\n",
    "        x = self.pool(x).squeeze() # [bs, embedding_dim, 1] ->[bs, embedding_dim] 每个样本变为一个向量，相当于是在词的数量上做了一个平均\n",
    "        # [bs, embedding_dim] -> [bs, hidden_dim]\n",
    "        x = self.layer(x)\n",
    "        x = self.fc(x) # [bs, hidden_dim] -> [bs, 1]\n",
    "        return x\n",
    "    \n",
    "       \n",
    "for key, value in AddingModel().named_parameters():\n",
    "    print(f\"{key:^40}paramerters num: {np.prod(value.shape)}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "outputs": [
    {
     "data": {
      "text/plain": "1024"
     },
     "execution_count": 33,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "16*64"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-04-30T08:35:46.539885600Z",
     "start_time": "2024-04-30T08:35:46.501910500Z"
    }
   }
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 训练"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-04-30T08:36:36.598870800Z",
     "start_time": "2024-04-30T08:36:35.491483900Z"
    }
   },
   "outputs": [],
   "source": [
    "from sklearn.metrics import accuracy_score\n",
    "\n",
    "@torch.no_grad()\n",
    "def evaluating(model, dataloader, loss_fct):\n",
    "    loss_list = []\n",
    "    pred_list = []\n",
    "    label_list = []\n",
    "    for datas, labels in dataloader:\n",
    "        datas = datas.to(device)\n",
    "        labels = labels.to(device)\n",
    "        # 前向计算\n",
    "        logits = model(datas)\n",
    "        loss = loss_fct(logits, labels)         # 验证集损失\n",
    "        loss_list.append(loss.item())\n",
    "        # 二分类\n",
    "        preds = logits > 0\n",
    "        pred_list.extend(preds.cpu().numpy().tolist())\n",
    "        label_list.extend(labels.cpu().numpy().tolist())\n",
    "        \n",
    "    acc = accuracy_score(label_list, pred_list)\n",
    "    return np.mean(loss_list), acc\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### TensorBoard 可视化\n",
    "\n",
    "\n",
    "训练过程中可以使用如下命令启动tensorboard服务。\n",
    "\n",
    "```shell\n",
    "tensorboard \\\n",
    "    --logdir=runs \\     # log 存放路径\n",
    "    --host 0.0.0.0 \\    # ip\n",
    "    --port 8848         # 端口\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-12-07T08:05:08.494890600Z",
     "start_time": "2023-12-07T08:05:08.455788500Z"
    }
   },
   "outputs": [],
   "source": [
    "from torch.utils.tensorboard import SummaryWriter\n",
    "\n",
    "\n",
    "class TensorBoardCallback:\n",
    "    def __init__(self, log_dir, flush_secs=10):\n",
    "        \"\"\"\n",
    "        Args:\n",
    "            log_dir (str): dir to write log.\n",
    "            flush_secs (int, optional): write to dsk each flush_secs seconds. Defaults to 10.\n",
    "        \"\"\"\n",
    "        self.writer = SummaryWriter(log_dir=log_dir, flush_secs=flush_secs)\n",
    "\n",
    "    def draw_model(self, model, input_shape):\n",
    "        self.writer.add_graph(model, input_to_model=torch.randn(input_shape))\n",
    "        \n",
    "    def add_loss_scalars(self, step, loss, val_loss):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/loss\", \n",
    "            tag_scalar_dict={\"loss\": loss, \"val_loss\": val_loss},\n",
    "            global_step=step,\n",
    "            )\n",
    "        \n",
    "    def add_acc_scalars(self, step, acc, val_acc):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/accuracy\",\n",
    "            tag_scalar_dict={\"accuracy\": acc, \"val_accuracy\": val_acc},\n",
    "            global_step=step,\n",
    "        )\n",
    "        \n",
    "    def add_lr_scalars(self, step, learning_rate):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/learning_rate\",\n",
    "            tag_scalar_dict={\"learning_rate\": learning_rate},\n",
    "            global_step=step,\n",
    "            \n",
    "        )\n",
    "    \n",
    "    def __call__(self, step, **kwargs):\n",
    "        # add loss\n",
    "        loss = kwargs.pop(\"loss\", None)\n",
    "        val_loss = kwargs.pop(\"val_loss\", None)\n",
    "        if loss is not None and val_loss is not None:\n",
    "            self.add_loss_scalars(step, loss, val_loss)\n",
    "        # add acc\n",
    "        acc = kwargs.pop(\"acc\", None)\n",
    "        val_acc = kwargs.pop(\"val_acc\", None)\n",
    "        if acc is not None and val_acc is not None:\n",
    "            self.add_acc_scalars(step, acc, val_acc)\n",
    "        # add lr\n",
    "        learning_rate = kwargs.pop(\"lr\", None)\n",
    "        if learning_rate is not None:\n",
    "            self.add_lr_scalars(step, learning_rate)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Save Best\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-12-07T08:05:08.499905900Z",
     "start_time": "2023-12-07T08:05:08.487901300Z"
    }
   },
   "outputs": [],
   "source": [
    "class SaveCheckpointsCallback:\n",
    "    def __init__(self, save_dir, save_step=5000, save_best_only=True):\n",
    "        \"\"\"\n",
    "        Save checkpoints each save_epoch epoch. \n",
    "        We save checkpoint by epoch in this implementation.\n",
    "        Usually, training scripts with pytorch evaluating model and save checkpoint by step.\n",
    "\n",
    "        Args:\n",
    "            save_dir (str): dir to save checkpoint\n",
    "            save_epoch (int, optional): the frequency to save checkpoint. Defaults to 1.\n",
    "            save_best_only (bool, optional): If True, only save the best model or save each model at every epoch.\n",
    "        \"\"\"\n",
    "        self.save_dir = save_dir\n",
    "        self.save_step = save_step\n",
    "        self.save_best_only = save_best_only\n",
    "        self.best_metrics = -1\n",
    "        \n",
    "        # mkdir\n",
    "        if not os.path.exists(self.save_dir):\n",
    "            os.mkdir(self.save_dir)\n",
    "        \n",
    "    def __call__(self, step, state_dict, metric=None):\n",
    "        if step % self.save_step > 0:\n",
    "            return\n",
    "        \n",
    "        if self.save_best_only:\n",
    "            assert metric is not None\n",
    "            if metric >= self.best_metrics:\n",
    "                # save checkpoints\n",
    "                torch.save(state_dict, os.path.join(self.save_dir, \"best.ckpt\"))\n",
    "                # update best metrics\n",
    "                self.best_metrics = metric\n",
    "        else:\n",
    "            torch.save(state_dict, os.path.join(self.save_dir, f\"{step}.ckpt\"))\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Early Stop"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-12-07T08:05:08.513991700Z",
     "start_time": "2023-12-07T08:05:08.502886500Z"
    }
   },
   "outputs": [],
   "source": [
    "class EarlyStopCallback:\n",
    "    def __init__(self, patience=5, min_delta=0.01):\n",
    "        \"\"\"\n",
    "\n",
    "        Args:\n",
    "            patience (int, optional): Number of epochs with no improvement after which training will be stopped.. Defaults to 5.\n",
    "            min_delta (float, optional): Minimum change in the monitored quantity to qualify as an improvement, i.e. an absolute \n",
    "                change of less than min_delta, will count as no improvement. Defaults to 0.01.\n",
    "        \"\"\"\n",
    "        self.patience = patience\n",
    "        self.min_delta = min_delta\n",
    "        self.best_metric = -1\n",
    "        self.counter = 0\n",
    "        \n",
    "    def __call__(self, metric):\n",
    "        if metric >= self.best_metric + self.min_delta:\n",
    "            # update best metric\n",
    "            self.best_metric = metric\n",
    "            # reset counter \n",
    "            self.counter = 0\n",
    "        else: \n",
    "            self.counter += 1\n",
    "            \n",
    "    @property\n",
    "    def early_stop(self):\n",
    "        return self.counter >= self.patience\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-12-07T08:08:51.411025300Z",
     "start_time": "2023-12-07T08:05:08.524001400Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": "  0%|          | 0/3920 [00:00<?, ?it/s]",
      "application/vnd.jupyter.widget-view+json": {
       "version_major": 2,
       "version_minor": 0,
       "model_id": "7ae9601ffb444363a0740715a696d7ae"
      }
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Early stop at epoch 15 / global_step 2940\n"
     ]
    }
   ],
   "source": [
    "# 训练\n",
    "def training(\n",
    "    model, \n",
    "    train_loader, \n",
    "    val_loader, \n",
    "    epoch, \n",
    "    loss_fct, \n",
    "    optimizer, \n",
    "    tensorboard_callback=None,\n",
    "    save_ckpt_callback=None,\n",
    "    early_stop_callback=None,\n",
    "    eval_step=500,\n",
    "    ):\n",
    "    record_dict = {\n",
    "        \"train\": [],\n",
    "        \"val\": []\n",
    "    }\n",
    "    \n",
    "    global_step = 0\n",
    "    model.train()\n",
    "    with tqdm(total=epoch * len(train_loader)) as pbar:\n",
    "        for epoch_id in range(epoch):\n",
    "            # training\n",
    "            for datas, labels in train_loader:\n",
    "                datas = datas.to(device)\n",
    "                labels = labels.to(device)\n",
    "                # 梯度清空\n",
    "                optimizer.zero_grad()\n",
    "                # 模型前向计算\n",
    "                logits = model(datas)\n",
    "                # 计算损失\n",
    "                loss = loss_fct(logits, labels)\n",
    "                # 梯度回传\n",
    "                loss.backward()\n",
    "                # 调整优化器，包括学习率的变动等\n",
    "                optimizer.step()\n",
    "                preds = logits > 0\n",
    "            \n",
    "                acc = accuracy_score(labels.cpu().numpy(), preds.cpu().numpy())    \n",
    "                loss = loss.cpu().item()\n",
    "                # record\n",
    "                \n",
    "                record_dict[\"train\"].append({\n",
    "                    \"loss\": loss, \"acc\": acc, \"step\": global_step\n",
    "                })\n",
    "                \n",
    "                # evaluating\n",
    "                if global_step % eval_step == 0:\n",
    "                    model.eval()\n",
    "                    val_loss, val_acc = evaluating(model, val_loader, loss_fct)\n",
    "                    record_dict[\"val\"].append({\n",
    "                        \"loss\": val_loss, \"acc\": val_acc, \"step\": global_step\n",
    "                    })\n",
    "                    model.train()\n",
    "                    \n",
    "                    # 1. 使用 tensorboard 可视化\n",
    "                    if tensorboard_callback is not None:\n",
    "                        tensorboard_callback(\n",
    "                            global_step, \n",
    "                            loss=loss, val_loss=val_loss,\n",
    "                            acc=acc, val_acc=val_acc,\n",
    "                            lr=optimizer.param_groups[0][\"lr\"],\n",
    "                            )\n",
    "                \n",
    "                    # 2. 保存模型权重 save model checkpoint\n",
    "                    if save_ckpt_callback is not None:\n",
    "                        save_ckpt_callback(global_step, model.state_dict(), metric=val_acc)\n",
    "\n",
    "                    # 3. 早停 Early Stop\n",
    "                    if early_stop_callback is not None:\n",
    "                        early_stop_callback(val_acc)\n",
    "                        if early_stop_callback.early_stop:\n",
    "                            print(f\"Early stop at epoch {epoch_id} / global_step {global_step}\")\n",
    "                            return record_dict\n",
    "                    \n",
    "                # udate step\n",
    "                global_step += 1\n",
    "                pbar.update(1)\n",
    "                pbar.set_postfix({\"epoch\": epoch_id})\n",
    "        \n",
    "    return record_dict\n",
    "        \n",
    "\n",
    "epoch = 20\n",
    "\n",
    "model = AddingModel()\n",
    "\n",
    "# 1. 定义损失函数 采用交叉熵损失 (但是二分类)\n",
    "loss_fct = F.binary_cross_entropy_with_logits\n",
    "# 2. 定义优化器 采用 adam\n",
    "# Optimizers specified in the torch.optim package\n",
    "optimizer = torch.optim.Adam(model.parameters(), lr=0.001)\n",
    "\n",
    "# 1. tensorboard 可视化\n",
    "if not os.path.exists(\"runs\"):\n",
    "    os.mkdir(\"runs\")\n",
    "tensorboard_callback = TensorBoardCallback(\"runs/imdb-adding\")\n",
    "# tensorboard_callback.draw_model(model, [1, MAX_LENGTH])\n",
    "# 2. save best\n",
    "if not os.path.exists(\"checkpoints\"):\n",
    "    os.makedirs(\"checkpoints\")\n",
    "save_ckpt_callback = SaveCheckpointsCallback(\"checkpoints/imdb-adding\", save_step=len(train_dl), save_best_only=True)\n",
    "# 3. early stop\n",
    "early_stop_callback = EarlyStopCallback(patience=5)\n",
    "\n",
    "model = model.to(device)\n",
    "record = training(\n",
    "    model, \n",
    "    train_dl, \n",
    "    test_dl, \n",
    "    epoch, \n",
    "    loss_fct, \n",
    "    optimizer, \n",
    "    tensorboard_callback=tensorboard_callback,\n",
    "    save_ckpt_callback=save_ckpt_callback,\n",
    "    early_stop_callback=early_stop_callback,\n",
    "    eval_step=len(train_dl)\n",
    "    )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-12-07T08:08:53.435894Z",
     "start_time": "2023-12-07T08:08:51.417022300Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": "<Figure size 720x360 with 2 Axes>",
      "image/png": "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\n"
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "#画线要注意的是损失是不一定在零到1之间的\n",
    "def plot_learning_curves(record_dict, sample_step=500):\n",
    "    # build DataFrame\n",
    "    train_df = pd.DataFrame(record_dict[\"train\"]).set_index(\"step\").iloc[::sample_step]\n",
    "    val_df = pd.DataFrame(record_dict[\"val\"]).set_index(\"step\")\n",
    "\n",
    "    # plot\n",
    "    fig_num = len(train_df.columns)\n",
    "    fig, axs = plt.subplots(1, fig_num, figsize=(5 * fig_num, 5))\n",
    "    for idx, item in enumerate(train_df.columns):    \n",
    "        axs[idx].plot(train_df.index, train_df[item], label=f\"train_{item}\")\n",
    "        axs[idx].plot(val_df.index, val_df[item], label=f\"val_{item}\")\n",
    "        axs[idx].grid()\n",
    "        axs[idx].legend()\n",
    "        # axs[idx].set_xticks(range(0, train_df.index[-1], 5000))\n",
    "        # axs[idx].set_xticklabels(map(lambda x: f\"{int(x/1000)}k\", range(0, train_df.index[-1], 5000)))\n",
    "        axs[idx].set_xlabel(\"step\")\n",
    "    \n",
    "    plt.show()\n",
    "\n",
    "plot_learning_curves(record, sample_step=10)  #横坐标是 steps"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 评估"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-12-07T08:08:59.407657100Z",
     "start_time": "2023-12-07T08:08:53.439892800Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "loss:     0.3085\n",
      "accuracy: 0.8785\n"
     ]
    }
   ],
   "source": [
    "# dataload for evaluating\n",
    "\n",
    "# load checkpoints\n",
    "model.load_state_dict(torch.load(\"checkpoints/imdb-adding/best.ckpt\", map_location=\"cpu\"))\n",
    "\n",
    "model.eval()\n",
    "loss, acc = evaluating(model, test_dl, loss_fct)\n",
    "print(f\"loss:     {loss:.4f}\\naccuracy: {acc:.4f}\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "pytorch",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.8"
  },
  "orig_nbformat": 4
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
